package util.sql;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Set;

import android.database.Cursor;
import util.sql.SqlField.KeyType;



public class SqlUtil {
	/**
	 * @param c
	 * @return the sql for creatint table
	 */
	final public static  String getSqlForCreateTable(Class<?> c){
		StringBuilder sb = new StringBuilder();
		sb.append("CREATE TABLE IF NOT EXISTS ");
		sb.append(getTableName(c));
		sb.append('(');
		Field[] fields = c.getDeclaredFields();
		for(Field field : fields){
			SqlField sm = field.getAnnotation(SqlField.class);
			if(null==sm){
				continue;
			}else{
				sb.append(getColumnName(field));
				sb.append(' ');
				Class<?> type = field.getType();
				if(isIntegerType(type)){
					sb.append(" INTEGER ");
				}else {
					sb.append(" TEXT ");
				}
				if(sm.type()==KeyType.AUTO_INCREMENT_KEY){
					sb.append("PRIMARY KEY AUTOINCREMENT");
				}else	if(sm.type()==KeyType.PRIMARY_KEY){
					sb.append("PRIMARY KEY");
				}
				sb.append(',');
			}
		}
		sb.setCharAt(sb.length()-1, ')');
		System.out.println(sb.toString());
		return sb.toString();
	}
	
	/**
	 * @param c
	 * @return a sql for insertint(replace) a piece of data item
	 */
	final public static  String getSqlForInsert(Object obj){
		try {
			StringBuilder sb = new StringBuilder();
			StringBuilder sbValues = new StringBuilder();
			String tableName = obj.getClass().getName().replace('.', '_');
			sb.append("REPLACE INTO ");
			sb.append(tableName);
			sb.append('(');
			Field[] fields = obj.getClass().getDeclaredFields();
			for(Field field : fields){
				SqlField sm = field.getAnnotation(SqlField.class);
				if(null==sm){
					continue;
				}else{
					Object value = field.get(obj);
					if(null!=value){
						if(sm.type()==KeyType.AUTO_INCREMENT_KEY){
							if((Integer)value<1){
								continue;
							}
						}
						sb.append(getColumnName(field));
						sb.append(',');
						Class<?> type = field.getType();
						if(isIntegerType(type)){
							sb.append((Long)value);
						}else{
							sbValues.append('\'');
							sbValues.append(value);
							sbValues.append('\'');
						}
						
						sbValues.append(',');
					}
				}
			}
			sb.setCharAt(sb.length()-1, ')');
			sbValues.setCharAt(sbValues.length()-1, ')');
			sb.append(" values (");
			sb.append(sbValues);
			System.out.println(sb.toString());
			return sb.toString();
		} catch (Exception e) {
			Log.exception(e);
		}
		return null;
	}

	final public static String getSqlForSelect(Object obj, Class<?> c){
		Field field = getPrimaryKeyField(c);
		if(null==field){
			throw new IllegalArgumentException("No primary key field declared for the class "+c);
		}else{
			return "select * from " +getTableName(c) + " where "+getColumnName(getPrimaryKeyField(c))+" = "+obj+" limit 1";
		}
	}
	
	final public static String getSqlForUpdate(Object obj){
		try {
			Field primaryKeyField = getPrimaryKeyField(obj.getClass());
			if(null==primaryKeyField){
				Log.error("No primary key anonation declared");
				return "";
			}
			StringBuilder sb = new StringBuilder();
			StringBuilder sbValues = new StringBuilder();
			String tableName = obj.getClass().getName().replace('.', '_');
			sb.append("update ");
			sb.append(tableName);
			sb.append(" set values (");
			Field[] fields = obj.getClass().getDeclaredFields();
			for(Field field : fields){
				SqlField sm = field.getAnnotation(SqlField.class);
				if(null==sm){
					continue;
				}else{
					Object value = field.get(obj);
					if(null!=value){
						String vs = String.valueOf(value);
						sb.append(getColumnName(field));
						sb.append(',');
						sbValues.append('\'');
						sbValues.append(vs);
						sbValues.append('\'');
						sbValues.append(',');
					}
				}
			}
			sb.setCharAt(sb.length()-1, ')');
			sbValues.setCharAt(sbValues.length()-1, ')');
			sb.append(" values (");
			sb.append(sbValues);
			sb.append(" where ");
			sb.append(primaryKeyField.getName());
			sb.append('=');
			sb.append(primaryKeyField.get(obj));
			System.out.println(sb.toString());
			return sb.toString();
		} catch (Exception e) {
			Log.exception(e);
		}
		return null;
	}
	
	final public static String getColumnName(Field field) {
		return "_"+field.getName();
	}
	
	final public static String getTableName(Class<?> c){
		return c.getName().replace('.', '_');
	}

	final public static String getSqlForDelete(Object obj){
		try {
			Field primaryKeyField = getPrimaryKeyField(obj.getClass());
			if(null==primaryKeyField){
				Log.error("No primary key anonation declared");
				return "";
			}
			StringBuilder sb = new StringBuilder();
			String tableName = obj.getClass().getName().replace('.', '_');
			sb.append("delete from ");
			sb.append(tableName);
			sb.append(" where ");
			sb.append(primaryKeyField.getName());
			sb.append(" =");
			sb.append(primaryKeyField.get(obj).toString());
			return sb.toString();
		} catch (Exception e) {
			Log.exception(e);
		}
		return null;
	}
	
	final private static Field getPrimaryKeyField(Class<?> c){
		Field[] fields = c.getDeclaredFields();
		for(Field field : fields){
			SqlField sf = field.getAnnotation(SqlField.class);
			if(null != sf && sf.type()!=KeyType.NOT_KEY){
				return field;
			}
		}
		return null;
	}
	
	final private static boolean isIntegerType(Class<?> type){
		return type==int.class || type==long.class || type==byte.class || type==short.class || type==Short.class ||type==char.class || type==Integer.class || type==Long.class || type==Byte.class || type==Character.class;
	}
	
	final public static <T> Object getInstance(Cursor cursor, Class<T> c){
		return getInstance(cursor, c, null);
	}
	
	final public static <T> Object getInstance(Cursor cursor, Class<T> c, HashMap<Field, Integer> columnMap){
		try {
			T t = c.newInstance();
			if(null==columnMap){
				Field[] fields = c.getFields();
				columnMap = new HashMap<Field, Integer>();
				for(Field field : fields){
					int column = cursor.getColumnIndex(SqlUtil.getColumnName(field));
					if(column!=-1){
						columnMap.put(field, cursor.getColumnIndex(SqlUtil.getColumnName(field)));
					}
				}
			}
			Set<Field> fields = columnMap.keySet();
			for(Field field : fields){
				Class<?> type = field.getType();
				Object value = null;
				int column = columnMap.get(field);
				if(type==int.class || type==Integer.class){
					value = cursor.getInt(column);
				}else if(type==long.class || type==Long.class){
					value = cursor.getLong(column);
				}else if(type==char.class || type==Character.class){
					value = (char) cursor.getInt(column);
				}else if(type==byte.class || type==Byte.class){
					value = (byte) cursor.getInt(column);
				}else if(type==boolean.class || type ==Boolean.class){
					value = "true".equals(cursor.getString(column));
				}else if(type==float.class || type==Float.class){
					value = (float) cursor.getDouble(column);
				}else if(type==short.class || type==Short.class){
					value = cursor.getShort(column);
				}else if(type==String.class){
					value = cursor.getString(column);
				}
				field.set(t, value);
			}
			return t;
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			Log.error("The java bean must have a public constructor without args");
			e.printStackTrace();
		}
		return null;
	}
	
//	final String getType(Field f){
//		Class<?> type = f.getType();
//		if(type==int.class || type==Integer.class){
//		}else if(type==long.class || type==Long.class){
//		}else if(type==char.class || type==Character.class){
//		}else if(type==byte.class || type==Byte.class){
//		}else if(type==boolean.class || type ==Boolean.class){
//		}else if(type==float.class || type==Float.class){
//		}else if(type==short.class || type==Short.class){
//		}else if(type==String.class){
//		}
//	}
}
